package com.kgd.news.utils.db;

import com.jfinal.log.Log;
import com.kgd.news.utils.DateUtils;
import com.kgd.news.utils.StringUtilsWx;
import com.kgd.news.utils.wx.WxHelper;

import java.sql.*;

public class InsertThread extends Thread {
    private  String update_sql;
    private String sql_data;
    private String table_name;
    private String sql_insert;
    private String sql_delete;
    private Connection conB;
    private String url_clear;
    private Connection conA;
    static int batchSize = 2500;
    // 线程计数器
    static private int threadCounts;
    // 线程名称池
    static private String threadNames[];
    private final static Log log = Log.getLog(InsertThread.class);

    //    静态代码块会在类被加载时自动执行，而无论加载者是JVM还是其他的类。
//    一个类中允许定义多个静态代码块，执行的顺序根据定义的顺序进行。
    static {
        // 假设这里允许系统同时运行最大线程数为10个
        int maxThreadCounts = 10;
        threadNames = new String[maxThreadCounts];
        // 初始化线程名称池
        for (int i = 1; i <= maxThreadCounts; i++) {
            threadNames[i - 1] = "子线程_" + i;
        }
    }

    public InsertThread() {
        // 临界资源锁定
        synchronized (InsertThread.class) {
            // 线程总数加1
            threadCounts++;
            // 从线程名称池中取出一个未使用的线程名
            for (int i = 0; i < threadNames.length; i++) {
                if (threadNames[i] != null) {
                    String temp = threadNames[i];
                    // 名被占用后清空
                    threadNames[i] = null;
                    // 初始化线程名称
                    this.setName(temp);
                    break;
                }
            }
        }
    }

    public void run() {
        try {
//            1900条media全部执行完要耗时90s左右，如果想节省时间，那么只有再开线程了吧。
//            int identifyIndex = getIdentifyIndexByTableName(table_name);
            Long start = System.currentTimeMillis();
            log.info(this.getName() + ",start：" + DateUtils.getOrderformat());
            if (!StringUtilsWx.isEmpty(update_sql)) {
//                写代码效率太低了，最好一次性能写好吧。
                Statement stmt_sourceB = conB.createStatement();
                int deleteStatus = stmt_sourceB.executeUpdate(update_sql);
                if (deleteStatus == 0) {
                }
                conB.commit();
                Long end = System.currentTimeMillis();
                System.out.println(this.getName() + ",耗时：" + (end - start) / 1000 + "秒");
                stmt_sourceB.close();
            } else {
                Statement stmt_source = conA.createStatement();
                ResultSet rs_sql_data = stmt_source.executeQuery(sql_data);
                Statement stmt_sourceB = conB.createStatement();
//            先删除
                if (!StringUtilsWx.isEmpty(sql_delete)) {
                    int deleteStatus = stmt_sourceB.executeUpdate(sql_delete);
                    if (deleteStatus == 0) {
                    }
//                stmt_sourceB.close();
//                stmt_sourceB = conB.createStatement();
                }
//            ResultSet rs_sql_dataB = stmt_sourceB.executeQuery(sql_data);
                ResultSetMetaData rsmd = rs_sql_data.getMetaData();
                PreparedStatement ps = conB.prepareStatement(sql_insert);
                int columnCount = rsmd.getColumnCount();
                int count = 1;
                StringBuilder existId = new StringBuilder();
//            while (rs_sql_dataB.next()) {
//                String id = rs_sql_dataB.getString(1);
//                existId.append(id + ",");
//            }
//            String existIdString = existId.toString();
                System.out.println(this.getName() + ",a：" + DateUtils.getOrderformat());
                while (rs_sql_data.next()) {
//                咋样判断是重复数据，还是要做一些特殊处理的，备份图片时，其他的也要备份的。
                    count++;
//                String id = rs_sql_data.getString(1);
//                if (existIdString.contains(id + ",")) {
//                    continue;
//                }
//                如果这个id是在b里面存在的
                    for (int k = 1; k <= columnCount; k++) {
//                    可以判断哪些列名
//                    String columnName = rsmd.getColumnName(k);
                        if (k == 1) {
                            ps.setString(k, rs_sql_data.getString(k));
                        } else {
                            String value = rs_sql_data.getString(k);
                            ps.setString(k, value);
//                        ps.setString(k-1, rs_sql_data.getString(k));
                        }
                    }
                    ps.addBatch();
                    if (count % batchSize == 0) {
                        ps.executeBatch();
                        conB.commit();
                    }
                }
//            两个线程一个在b-b1间耗时，一个在b1-c间耗时，结束时间基本相同，对应同一个数据库的不同的表
                log.info(this.getName() + ",b：" + DateUtils.getOrderformat());
                ps.executeBatch();
                log.info(this.getName() + ",b1：" + DateUtils.getOrderformat());
                conB.commit();
                log.info(this.getName() + ",c：" + DateUtils.getOrderformat());
                Long end = System.currentTimeMillis();
                log.info(this.getName() + ",耗时：" + (end - start) / 1000 + "秒");
                stmt_source.close();
                stmt_sourceB.close();
                rs_sql_data.close();
                ps.close();
            }
        } catch (Exception e) {
            System.out.println(e);
        } finally {
//            当run() 或者 call() 方法执行完的时候线程会自动结束,如果要手动结束一个线程
            synchronized (InsertThread.class) {
                // 释放线程名称
                String[] threadName = this.getName().split("_");
                // 线程名使用完后放入名称池
                threadNames[Integer.parseInt(threadName[1]) - 1] = this.getName();
                // 线程运行完毕后减1
                threadCounts--;
                WxHelper.requestUrl(url_clear);
                /*
                 * 通知其他被阻塞的线程，但如果其他线程要执行，则该同步块一定要运行结束（即直
                 * 到释放占的锁），其他线程才有机会执行，所以这里的只是唤醒在此对象监视器上等待
                 * 的所有线程，让他们从等待池中进入对象锁池队列中，而这些线程重新运行时它们一定
                 * 要先要得该锁后才可能执行，这里的notifyAll是不会释放锁的，试着把下面的睡眠语
                 * 句注释去掉，即使你已调用了notify方法，发现CreateThread中的同步块还是好
                 * 像一直处于对象等待状态，其实调用notify方法后，CreateThread线程进入了对象锁
                 * 池队列中了，只要它一获取到锁，CreateThread所在线程就会真真的被唤醒并运行。
                 */
                InsertThread.class.notifyAll();
                System.out.println("----" + this.getName() + " 所占用资源释放完毕，当前系统正在运行的子线程数："
                        + threadCounts);
            }
        }
    }

    private int getIdentifyIndexByTableName(String table_name) {
//如果存在的话，那是要覆盖的吧，就是先删除：删除时删除正确就可以了，根据，再添加
        if ("news".equals(table_name)) {
            return 3;
        }
        return 2;
    }


    static public int getThreadCounts() {
        synchronized (InsertThread.class) {
            return threadCounts;
        }
    }

    public InsertThread(String sql_data, String sql_insert, String sql_delete, Connection conB, Connection conA, String url_clear,String table_name) {
        super();
        this.sql_data = sql_data;
        this.sql_delete = sql_delete;
        this.sql_insert = sql_insert;
        this.table_name = table_name;
        this.conB = conB;
        this.conA = conA;
        this.url_clear = url_clear;
        // 临界资源锁定
        synchronized (InsertThread.class) {
//            这个样子的化，只能一个一个来的，
            // 线程总数加1
            threadCounts++;
            // 从线程名称池中取出一个未使用的线程名
            for (int i = 0; i < threadNames.length; i++) {
                if (threadNames[i] != null) {
                    String temp = threadNames[i];
                    // 名被占用后清空
                    threadNames[i] = null;
                    // 初始化线程名称
                    this.setName(temp);
                    break;
                }
            }
        }
    }


    public InsertThread( Connection conB,  String table_name,String update_sql,String url_clear) {
        super();
        this.url_clear = url_clear;
        this.update_sql = update_sql;
        this.table_name = table_name;
        this.conB = conB;
        // 临界资源锁定
        synchronized (InsertThread.class) {
//            这个样子的化，只能一个一个来的，
            // 线程总数加1
            threadCounts++;
            // 从线程名称池中取出一个未使用的线程名
            for (int i = 0; i < threadNames.length; i++) {
                if (threadNames[i] != null) {
                    String temp = threadNames[i];
                    // 名被占用后清空
                    threadNames[i] = null;
                    // 初始化线程名称
                    this.setName(temp);
                    break;
                }
            }
        }
    }
}
